/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is the ViewMyCurrency extension.
 *
 * The Initial Developer of the Original Code is
 * Will Moffat.
 * Portions created by the Initial Developer are Copyright (C) 2006
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

//dump("// $Id: WILL_PrefManager.js,v 1.58 2007/02/28 11:18:31 Will Exp $;\n");

// (many thanks for GreaseMonkey for getting me started with preferences)

// ViewMyCurrency specific preferences management
VMC.PrefManager = function(startpoint) {
  var self=this;

  //WILL: TODO: consider using Mozilla Default support for default values!

  //default options
  var SOURCE_CURRENCY_LIST_COUNTRIES=['United States','Eurozone','United Kingdom','Japan',
				      'Canada','Australia','Switzerland','China, People\'s Republic of'];
  var TARGET_CURRENCY_LIST_COUNTRIES=['United States'];
  
  var EARLIEST_COMPATIBLE_VERSION = 0.3895; // symbol removed from currencies
  var DEFUALT_DEBUG=0; // default: only show warnings and errors
  var DEFAULT_LAYOUTORDER = false; // brackets: new currency (old currency)
  var DEFAULT_HIGHLIGHT_CHANGES = false; // don't show red borders around changed elements
  var DEFAULT_TOOLTIP_MODE      = false; // normal mode, don't use tooltips

  this._ps = new VMC.PrefService(startpoint || 'extensions.viewmycurrency.'); // default normally used


  //////////////////////
  // public functions //
  //////////////////////
  this.init = function() {
    this._keys=VMC.PrefManager.keys;

    this.requiredPrefs=[this._keys.TARGET_CURRENCY_CODE, this._keys.SOURCE_CURRENCY_LIST,  this._keys.TARGET_CURRENCY_LIST];
    this.optionalPrefs=[this._keys.HIDE_ORIGINAL_CURRENCY, this._keys.DEBUG_LEVEL, this._keys.HIDE_IN_STATUSBAR, this._keys.LAYOUT_ORDER,
			this._keys.DECIMAL_SEPARATOR, this._keys.THOUSANDS_SEPARATOR, this._keys.FORCE_SPACE, this._keys.FORCE_SYMBOL_POS,
			this._keys.HIGHLIGHT_CHANGES, this._keys.TOOLTIP_MODE, this._keys.ROUND_NUMBERS
			];
    return this;
  };


  this.determineStatusOfExistingPrefs = function() {
    var storedKey;
    try {
      if (!this._ps.branchHasChildren('')) { return VMC.PrefManager.stateVirgin; }
      
      //else there are some prefs here

      //let's check which version of VMC wrote these prefs
      var keyStr=this.getCurrentVersion(true); // fail silently
      if (!keyStr) { return VMC.PrefManager.stateCannotDetermineVersion; }
      storedKey = Number(keyStr).valueOf();
      if (storedKey < EARLIEST_COMPATIBLE_VERSION) { return VMC.PrefManager.stateIncompatibleOlderVersion; }
      //WILL: TODO: what if it's greater than currency version?
      
      // ok, the prefs claim to be written by a suitable version of VMC.
      // let's check we can read everything in.
      var missingReqPrefs=this.missingRequiredPrefs();
      if (missingReqPrefs.length) { return VMC.PrefManager.stateCorrupt; }


      // check if any of the optional prefs are missing, and if so, restore them
      var missingOptPrefs=this.missingOptionalPrefs();
      if (missingOptPrefs) { this.restoreOptionalPrefs(); }

    } catch (e) {
      VMC.log('ERR','Was not expecting exception in prefs '+e);
      return VMC.PrefManager.stateCorrupt;
    }

    if (storedKey < VMC.VERSION) {
      // ooh exciting ... we've been upgraded
      return VMC.PrefManager.stateValidPrefsAndVmcUpgraded;
    }
    
    return VMC.PrefManager.stateValidPrefsPresent;
  };

    
    
  
//   // on application startup we do these checks but we don't change anything (autoCancel)
//   // once we know the status, we do the checks again when the user clicks on me.
//   this.doStartupChecks = function(autoCancel) {
//     var ALREADY_INSTALLED = "ALREADY_INSTALLED"; // compatible version is already installed (normal case on startup!)
//     var DONT_INSTALL="DONT_INSTALL"; // invalid prefs --> first time (missing or old version num) or (user didn't want to delete old prefs and start again)
//     var TO_INSTALL = "TO_INSTALL";  // first time (everything empty) or either case of DONT_INSTALL but user anwswers OK.

//     // Do the version check
//     if (!this._ps.exists(this._keys.CURRENT_VERSION)) {   // we are missing the version number
//       // but maybe there is some old junk lying  around?
//       if (!this._checkPromptUserAndDeleteBranch(autoCancel,STARTPOINT)) { return DONT_INSTALL; }
//       else { return TO_INSTALL; } // to install (no version num)
//     }

//     //version is key present

//     var storedVersion = Number(this._ps.getString(this._keys.CURRENT_VERSION)).valueOf();
//     if (storedVersion < EARLIEST_COMPATIBLE_VERSION) {
//       if (!this._checkPromptUserAndDeleteBranch(autoCancel,STARTPOINT)) { return DONT_INSTALL; }
//       else { return TO_INSTALL; } // to re-install (out of date)
//     }

//     // version was up to date
    
//     var missingReqPrefs=this.missingRequiredPrefs();
//     if (missingReqPrefs.length) {
//       if (!this._checkPromptUserAndDeleteBranch(autoCancel,STARTPOINT,"Required ViewMyCurrency preferences have been deleted.")) { return DONT_INSTALL; }
//       else { return TO_INSTALL; } // to re-install (missing prefs)
//     }

//     // check if any of the optional prefs are missing, and if so, restore them
//     var missingOptPrefs=this.missingOptionalPrefs();
//     if (missingOptPrefs) { this.restoreOptionalPrefs(); }

//     // version was up to date. No further setup action requred
//     return ALREADY_INSTALLED;
//   };
   
  
  this.storeInitialPrefs = function(targetCurrencyList,deleteOldPrefs) {

    // The click finished the setup wizard.
    if (deleteOldPrefs) {
      this._ps.deleteBranch(''); // delete all the old VMC prefs
    }

    // record which version this is
    this._ps.setString(this._keys.CURRENT_VERSION,VMC.VERSION);

    //Store all the prefs
    this.restoreDefaultPrefs();
    this.restoreOptionalPrefs();

    if (targetCurrencyList) {
      this.setTargetCurrencyList( targetCurrencyList );
      //WILL: NO, this should be controlled by the gui this.setTargetCurrency( newTargetCurrencyList.items[0] ); //start converting
    }
  };

  // we seperate required and optional prefs, missing optional prefs can be restored without asking the user
  this.restoreOptionalPrefs = function() { //WILL: TODO: check if missing 
    this.setDebugLevel(DEFUALT_DEBUG);
    this.setHideOriginalCurrency(false); // no hide
    this.setRoundNumbers(true);          // round numbers by default
    this.setHideInStatusbar(false);      // show VMC in statusbar
    this._restoreDefaultNumberFormat();  // try to guess what number format this user wants
    this.setForceSpace(false);           // no forced gap between currency symbol and number
    this.setLayoutOrder(DEFAULT_LAYOUTORDER);
    this.setHighlightChanges(DEFAULT_HIGHLIGHT_CHANGES);
    this.setTooltipMode(DEFAULT_TOOLTIP_MODE);
    this.setForceSymbolPos('RESPECT');   // preserve the order of the currency symbol and number
  };
  
  this._restoreDefaultNumberFormat = function() {
    var thousandsSeparator = ',';
    var decimalSeparator   = '.'; // default to US/UK number format
    var numStr=new Number(1234.56).toLocaleString();
    numStr.replace(/\d/g,''); //remove all digits
    if (numStr.length==2) {
      thousandsSeparator = numStr[0];
      decimalSeparator   = numStr[1];
    }
    this.setDecimalSeparator(decimalSeparator);
    this.setThousandsSeparator(thousandsSeparator);
  };
  
  this.getDefaultSourceListPref   = function() { return VMC.currencyDB.createListFromCountries(SOURCE_CURRENCY_LIST_COUNTRIES); };
  this.getDefaultTargetListPref   = function() { return VMC.currencyDB.createListFromCountries(TARGET_CURRENCY_LIST_COUNTRIES); };

  this.restoreDefaultPrefs = function() {
    this.setSourceCurrencyList(   this.getDefaultSourceListPref()   );
    this.setTargetCurrencyList(   this.getDefaultTargetListPref()   );
    this.setTargetCurrencyCode(null); //disabled by default
    //    this.resetLastUpdateAttemptTime();
  };

  this.setDebugLevel = function(level)  { this._ps.setString(this._keys.DEBUG_LEVEL,level);      };
  this.getDebugLevel = function()       { return Number(this._ps.getString(this._keys.DEBUG_LEVEL, DEFUALT_DEBUG)).valueOf(); }; 

  this.setDecimalSeparator   = function(separator) { this._ps.setString(this._keys.DECIMAL_SEPARATOR,   escape(separator) );  };
  this.setThousandsSeparator = function(separator) { this._ps.setString(this._keys.THOUSANDS_SEPARATOR, escape(separator) );  };

  this.getDecimalSeparator   = function() { return unescape(this._ps.getString(this._keys.DECIMAL_SEPARATOR)  ,'.'); };  // default to DOT 
  this.getThousandsSeparator = function() { return unescape(this._ps.getString(this._keys.THOUSANDS_SEPARATOR),','); };  // default to COMMA (UK style)


  ///////////////////////////////////////////
  // Boolean prefs (they are easy to code) //
  ///////////////////////////////////////////
  this.getHideOriginalCurrency = function()        { return this._ps.getBool(this._keys.HIDE_ORIGINAL_CURRENCY,false); }; // default to show currency
  this.setHideOriginalCurrency = function(enabled) { this._ps.setBool(this._keys.HIDE_ORIGINAL_CURRENCY,enabled); };

  this.getRoundNumbers         = function()        { return this._ps.getBool(this._keys.ROUND_NUMBERS,false); }; // default to show currency
  this.setRoundNumbers         = function(enabled) { this._ps.setBool(this._keys.ROUND_NUMBERS,enabled); };

  this.getHideInStatusbar      = function()        { return this._ps.getBool(this._keys.HIDE_IN_STATUSBAR,false); };      // default to show in statubar
  this.setHideInStatusbar      = function(enabled) { this._ps.setBool(this._keys.HIDE_IN_STATUSBAR,enabled); };

  this.getForceSpace           = function()        { return this._ps.getBool(this._keys.FORCE_SPACE, false);      }; // default to false
  this.setForceSpace           = function(enabled) { this._ps.setBool(this._keys.FORCE_SPACE, enabled); };    

  this.getLayoutOrder          = function()        { return this._ps.getBool(this._keys.LAYOUT_ORDER, DEFAULT_LAYOUTORDER);  };
  this.setLayoutOrder          = function(enabled) { this._ps.setBool(this._keys.LAYOUT_ORDER, enabled); };    

  this.getHighlightChanges     = function()        { return this._ps.getBool(this._keys.HIGHLIGHT_CHANGES, DEFAULT_HIGHLIGHT_CHANGES);  };
  this.setHighlightChanges     = function(enabled) { this._ps.setBool(this._keys.HIGHLIGHT_CHANGES, enabled); };    

  this.getTooltipMode          = function()        { return this._ps.getBool(this._keys.TOOLTIP_MODE, DEFAULT_TOOLTIP_MODE);  };
  this.setTooltipMode          = function(enabled) { this._ps.setBool(this._keys.TOOLTIP_MODE, enabled); };    

  
  // String prefs
  this.getForceSymbolPos       = function()        { return this._ps.getString(this._keys.FORCE_SYMBOL_POS, 'RESPECT'); };  // default to RESPECT
  this.setForceSymbolPos       = function(pos)     { this._ps.setString(this._keys.FORCE_SYMBOL_POS, pos); };    


  // list funcs
  this.setSourceCurrencyList   = function(list) { this.setCurrencyList(this._keys.SOURCE_CURRENCY_LIST,list);    };
  this.setTargetCurrencyList   = function(list) { this.setCurrencyList(this._keys.TARGET_CURRENCY_LIST,list);    };

  this.getSourceCurrencyList   = function()     { return this.getCurrencyList(this._keys.SOURCE_CURRENCY_LIST);   };
  this.getTargetCurrencyList   = function()     { return this.getCurrencyList(this._keys.TARGET_CURRENCY_LIST);   };

  this.readSourceCurrencyList  = function(list) { this.readCurrencyList(this._keys.SOURCE_CURRENCY_LIST,list);   };
  this.readTargetCurrencyList  = function(list) { this.readCurrencyList(this._keys.TARGET_CURRENCY_LIST,list);   };

  this.setCurrencyList = function(key,currencyList) { this._ps.setString(key,currencyList.toString(true));   };
  this.getCurrencyList = function(key)              { return new VMC.CurrencyList(this._ps.getString(key)); };

  this.readCurrencyList = function(key,list) {
    //WILL: TODO: think about when this can happen: if (VMC.CurrencyList.prototype.isValid(list,true)) {VMC.log('WARN','WILL: TODO: we are trashing valid list'); }
    list.initFrom(this._ps.getString(key));
    VMC.CurrencyList.prototype.isValid(list); // assert it's OK
  };

  this.missingRequiredPrefs = function () {
    var missing='';
    var key;
    for (var i=0; i<this.requiredPrefs.length; i++) {
      key=this.requiredPrefs[i];
      if (!this._ps.exists(key)) { missing=missing+key+','; }
    }
    return missing;
  };

  this.missingOptionalPrefs = function () {
    var missing='';
    var key;
    for (var i=0; i<this.optionalPrefs.length; i++) {
      key=this.optionalPrefs[i];
      if (!this._ps.exists(key)) { missing=missing+key+','; }
    }
    return missing;
  };

  //WILL: we use the currency.code as a unique identifier, this means that targetList must have unqiue entries
  this.getTargetCurrencyCode = function () { return this._ps.getString(this._keys.TARGET_CURRENCY_CODE);      };
  this.setTargetCurrencyCode = function (code) {    this._ps.setString(this._keys.TARGET_CURRENCY_CODE, code || ''); }; 

  this.getCurrentVersion = function(failSilently) {
    if (this._ps.exists(this._keys.CURRENT_VERSION)) {
      return this._ps.getString(this._keys.CURRENT_VERSION);
    }
    if (failSilently) { return null; }
    VMC.log('ERR','Missing current version '+this._keys.CURRENT_VERSION+' in prefs');
    return undefined;
  };

  this.setCurrentVersion = function() {
    this._ps.setString(this._keys.CURRENT_VERSION,VMC.VERSION);
  };

  //WILL:  'self' is required as these are executed in a callback
  this.readRate = function(rateObj,failSilently) {
    var key=self._keys.RATE+rateObj.toCurrency.code+'/'+rateObj.fromCurrency.code;
    if (!self._ps.exists(key)) {
      if (!failSilently) { VMC.log('WARN','PrefManager: missing '+key); }
      return false;
    }
    rateObj.updateFromStr(self._ps.getString(key));
    VMC.log('DBG','PrefManager: retrieved '+rateObj);
    return true;
  };

  this.writeRate = function(rateObj) {
    if (!VMC.ExchangeRate.isSaneValue(rateObj.rate)) { VMC.log('WARN','Writing insane rate '+rateObj.toString(true)); }
    var key=self._keys.RATE+rateObj.toCurrency.code+'/'+rateObj.fromCurrency.code;
    self._ps.setString(key,rateObj.toString(true)); //store encoded rate object
  };

  this._watchFuncs = []; //WILL: TODO: it should be possible to remove just some observers. I don't need this funcitonailty yet.

  //WILL: call watcherFuncStr if pref changes. WILL: TODO: why couldn't I just use  //watcherFuncStr.call(contextObj,prefName);
  this._addWatcher = function(prefName, contextObj,watcherFuncStr) {
    //dump('adding watcher '+watcherFuncStr+' to '+prefName+'\n');
    // construct an observer
    var observer = {
      observe:function(subject, topic, prefName) {
	var callwrapper = new VMC.CCallWrapper(contextObj, 10, watcherFuncStr, prefName); //after 10 ms delay call contextObj.'watcherFuncStr'(prefName)
	VMC.CCallWrapper.asyncExecute(callwrapper);
      }
    };
    
    // store the observer in case we need to remove it later
    this._watchFuncs.push([prefName,observer]);

    VMC.log('DBG','Watching '+prefName);
    this._ps.addObserver(prefName,observer);

  };
  
  this.registerWatcher = function(context,interestingKeys,func) {
    for (var i=0;i<interestingKeys.length;i++) {
      var key=interestingKeys[i];
      if (!key) { VMC.log('ERR','Tried to register non existent key'); }
      this._addWatcher(key,context,func); 
    }
  };

  // stop watching
  this.unregisterWatcher = function() {
    var observers = this._watchFuncs;
    if (!observers) { VMC.log('ERR','no watchers to remove!'); }
    for (var i=0;i<observers.length;i++) {
      var a=observers[i];
      var prefName=a[0];
      var observer=a[1];
      VMC.log('DBG','Un-watching '+prefName);
      this._ps.removeObserver(prefName,observer);
    }
      
//     var prefName=this.func2pref[watcherFuncStr];
//     if (this.observers[watcherFuncStr]) {
//       this._ps.removeObserver(prefName, this.observers[watcherFuncStr]);
//     } else {
//       VMC.alert('tried to remove unknown watcherFuncStr for pref '+prefName);
//     }
  };



//   this._checkPromptUserAndDeleteBranch = function(autoCancel,startpoint,messageStart,autoOK) {
//     var root = Components.classes["@mozilla.org/preferences-service;1"]. getService(Components.interfaces.nsIPrefBranch);
//     var children = root.getChildList(startpoint, {});
//     if (!children.length) { return true;  } //branch doesn't exist
//     if (autoCancel)       { return false; } //branch exists and has not been deleted

//     var message= messageStart || 'Incompatible preferences from an older version of ViewMyCurrency have been detected.\n';
//     message=message+ '\nDelete the '+children.length+' items stored in "'+startpoint+'" ?';

//     var result;
//     if (!autoOK) {
//       var prompts = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].getService(Components.interfaces.nsIPromptService);
//       result = prompts.confirm(null, "Delete old preferences", message);
//     }

//     if (result || autoOK) {
//       //alert('deleting '+children.join(','));
//       root.deleteBranch(startpoint);
//       return true; //they existed but now they are gone
//     } else {
//       return false; //user clicked cancel - prefs exist but not deleted
//     }

//   };

  // returns true if we deleted all prefs
  this.deleteViewMyCurrencyPrefs = function(extraMessage) {
    var prompts = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].getService(Components.interfaces.nsIPromptService);
    var message = "This will delete all preferences associated with ViewMyCurrency.";
    if (extraMessage) {message += extraMessage;}

    var result = prompts.confirm(null, "Delete all ViewMyCurrency prefs?",message);
    if (!result) {
      return false; //don't do it!
    }

    this._ps.deleteBranch(''); // delete all VMC prefs
    return true;
  };

  this.readTableFile = function() {
    var file = this.getConversionTableFile();
    //vmc.hack('file='+file.path);
    var table = null;
    // |file| is nsIFile
    var data = "";
    var fstream = Components.classes["@mozilla.org/network/file-input-stream;1"]
                            .createInstance(Components.interfaces.nsIFileInputStream);
    var sstream = Components.classes["@mozilla.org/scriptableinputstream;1"]
                            .createInstance(Components.interfaces.nsIScriptableInputStream);
    try {
      fstream.init(file, -1, 0, 0);
      sstream.init(fstream); 
      
      var str = sstream.read(4096);
      while (str.length > 0) {
	data += str;
	str = sstream.read(4096);
      }
      
      sstream.close();
      fstream.close();
      table = eval(data,VMC); //evaulation in the context of VMC //TODO: eval in sandbox?? about:blank // or move to JSON??
    } catch (e) {
      VMC.log('ERR',' readTable got '+e);
    }
    return table;
  };

  this.writeTableFile = function(table) {
    var file = this.getConversionTableFile();
    var data = VMC.escapeUnicode(table.toString());
    // file is nsIFile, data is a string
    var foStream = Components.classes["@mozilla.org/network/file-output-stream;1"]
                             .createInstance(Components.interfaces.nsIFileOutputStream);

    // use 0x02 | 0x10 to open file for appending.
    foStream.init(file, 0x02 | 0x08 | 0x20, 0664, 0); // write, create, truncate
    foStream.write(data, data.length);
    foStream.close();
    //WILL: TODO: error handling??
  };

  this.getConversionTableFile = function() {
    // get profile directory
    var file = Components.classes["@mozilla.org/file/directory_service;1"]
                         .getService(Components.interfaces.nsIProperties)
                         .get("ProfD", Components.interfaces.nsIFile);
    file.append('extensions');
    file.append('ViewMyCurrency_profiles.js');
    return file;
  };

  this.getDefaultConversionTable = function() {
    
    var makeConversionProfileTableEntry = function(sourceCurrencyList, profileName, urlRegexps) {
      var symbolTable = VMC.currencyDB.generateDefaultSymbolTableFor(sourceCurrencyList);
      var conversionProfile = new VMC.ConversionProfile(symbolTable);
      var tableEntry = new VMC.ConversionProfileTableEntry(profileName,urlRegexps,conversionProfile);
      return tableEntry;
    };
        
    var regexps = [ /^chrome:\/\/(?!jsunit)/,  //no chrome unless chrome://jsunit
		    /^https/,                  // no banking sites
		    /^http:\/\/viewmycurrency/ // no VMC sites, it gets too confusing!
		    ];
    var excludedEntry = new VMC.ConversionProfileTableEntry('ExcludedSites', regexps , null ); // null profile
  
    var countries;
    countries = ['United States','Canada','Australia', 'Eurozone','United Kingdom','Japan','Switzerland','China, People\'s Republic of'];
    var defaultEntry = makeConversionProfileTableEntry( VMC.currencyDB.createListFromCountries(countries),
							'Default', null );
    
    countries = ['Canada','United States','Australia', 'Eurozone','United Kingdom','Japan','Switzerland','China, People\'s Republic of'];
    var canadianEntry = makeConversionProfileTableEntry( VMC.currencyDB.createListFromCountries(countries),
							 'Canadian', [ /\.ca($|\/)/, /\/ca\./, /canada/ ] );
    
    countries = ['Australia', 'United States','Canada','Eurozone','United Kingdom','Japan','Switzerland','China, People\'s Republic of'];
    var audEntry = makeConversionProfileTableEntry( VMC.currencyDB.createListFromCountries(countries),
						    'Australian', [ /\.au($|\/)/, /\/au\./ ] );
    
    // make the profile table
    var table = new VMC.ConversionProfileTable( [excludedEntry, canadianEntry, audEntry, defaultEntry] );
    
    return table;
  };


};

// Class vars
VMC.PrefManager.stateVirgin="Never seen VMC";
VMC.PrefManager.stateCannotDetermineVersion="Version key missing";
VMC.PrefManager.stateIncompatibleOlderVersion="Prefs are from an older, incompatible version";
VMC.PrefManager.stateCorrupt="Something went wrong during pref detection!";
VMC.PrefManager.stateValidPrefsPresent="All prefs look OK";
VMC.PrefManager.stateValidPrefsAndVmcUpgraded="Prefs OK and VMC has been upgraded!";

VMC.PrefManager.keys = {
  RATE                    : 'fx.rate.',
  CURRENT_VERSION         : 'currentversion',
  
  SOURCE_CURRENCY_LIST    : 'list.sourceCurrencyList',
  TARGET_CURRENCY_LIST    : 'list.targetCurrencyList',
  
  TARGET_CURRENCY_CODE    : 'target_currency_code',
  HIDE_ORIGINAL_CURRENCY  : 'hide_original_currency',
  ROUND_NUMBERS           : 'round_numbers',
  HIDE_IN_STATUSBAR       : 'hide_in_statusbar',
  DECIMAL_SEPARATOR       : 'decimal_separator',   
  THOUSANDS_SEPARATOR     : 'thousands_separator',
  FORCE_SPACE             : 'force_space',
  LAYOUT_ORDER            : 'layout_order',
  FORCE_SYMBOL_POS        : 'force_symbol_pos',
  HIGHLIGHT_CHANGES       : 'highlight_changes',
  TOOLTIP_MODE            : 'tooltip_mode',
  DEBUG_LEVEL             : 'debug_level'
};




  ///////////////////////////////
  // Low-level pref management //
  ///////////////////////////////
VMC.PrefService = function(startpoint) {

  var tmpFile = null; // used for storing backup prefs. Only needed during testing


  var prefService =Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefService);
  this._prefRoot = prefService.getBranch(startpoint);

  function getTmpFile() { //create the temp file only once
    if (!tmpFile) {
      tmpFile = Components.classes["@mozilla.org/file/directory_service;1"]
	                  .getService(Components.interfaces.nsIProperties)
                          .get("TmpD", Components.interfaces.nsIFile);
      tmpFile.append("VmcBackupPrefs.tmp");
      tmpFile.createUnique(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0664);
    }
    return tmpFile;
  }

  this.storePrefs = function() {
    var file = getTmpFile();
    prefService.savePrefFile(file);
    //vmc.hack('INFO','Prefs saved in '+file.path);
  };

  this.restorePrefs = function() {
    var file = getTmpFile();
    prefService.readUserPrefs(file);
    //vmc.hack('INFO','Prefs restored from '+file.path);
  };
  

  this.deleteBranch = function(branch) { this._prefRoot.deleteBranch(branch); };

  this.getPrefChildren = function(branch) {
    var children = this._prefRoot.getChildList(branch, {});
    return children;
  };

  this.branchHasChildren = function(branch) {
    var children=this.getPrefChildren(branch);
    if (!children || !children.length) { return false; }
    return true;
  };

  this.exists = function(key) {
    var prefType = this._prefRoot.getPrefType(key);
    if (prefType == this._prefRoot.PREF_INVALID) {
      return false;
    } else {
      return true;
    }
  };


  //WILL: acording do the mozilla docs I should use nsIPrefBranch2 but Firefox 1.0.7 doesn't support it
  this.addObserver = function(prefName,observer) {
    if (!prefName || !observer) { VMC.log('ERR','missing args'); }
    this._prefRoot.QueryInterface(Components.interfaces.nsIPrefBranchInternal).addObserver(prefName, observer, false);
  };

  this.removeObserver = function(prefName,observer) {
    if (!prefName || !observer) { VMC.log('ERR','missing args'); }
    this._prefRoot.QueryInterface(Components.interfaces.nsIPrefBranchInternal).removeObserver(prefName, observer);
  };

  /////////////////
  // The setters //
  /////////////////
  this.setBool = function(key,bool) {
    VMC.log('DBG','PrefManager: Storing '+key+'='+bool);
    try {
      this._prefRoot.setBoolPref(key, bool);
    } catch(e) {
      VMC.log('ERR','PrefManager: Failed to store '+key+'='+bool);
    }
  };
  this.setString = function(key,str) { 
    VMC.log('DBG','PrefManager: Storing '+key+'='+str);
    try {
      this._prefRoot.setCharPref(key, str);
    } catch(e) {
      VMC.log('ERR','PrefManager: Failed to store '+key+'='+str);
    }
  };
   
  /////////////////
  // The getters //
  /////////////////
  this.getBool = function(key,defaultVal) {
    try {
      if (this.exists(key)) {
	return this._prefRoot.getBoolPref(key);
      }
    } catch(e) { VMC.log('WARN','Caught '+e); }
    // else pref was missing
    if (defaultVal === undefined) {
      VMC.log('ERR','PrefManager: pref does not exist ['+key+']');
      return null;
    } else {
      return defaultVal;
    }
  };

  this.getString = function(key,defaultVal) {
    try {
      if (this.exists(key)) {
	return this._prefRoot.getCharPref(key);
      }
    } catch(e) { VMC.log('WARN','Caught '+e); }
    // else pref was missing
    if (defaultVal === undefined) {
      VMC.log('ERR','PrefManager: pref does not exist ['+key+']');
      return null;
    } else {
      return defaultVal;
    }
  };


};
